use crate::constant::Constant;
use crate::reader::Stream;

pub enum Attribute {
	Constant(AttrConstant),
	NotSurport(String),
}
impl std::fmt::Debug for Attribute {
	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
		match self {
			Self::Constant(arg0) => f.write_fmt(format_args!("{}: {};", arg0.fmt_type("i32"), arg0.fmt_value("i32"))),
			Self::NotSurport(arg0) => f.debug_tuple("NotSurport").field(arg0).finish(),
		}
	}
}
impl Attribute {
	pub fn parse(s: &mut Stream, constants: &Vec<Constant>) -> Option<Self> {
		let name_index: u16 = s.read()?;
		let name = constants.get(name_index as usize)?.to_string(constants)?;
		let length: u32 = s.read()?;
		Some(match name.as_str() {
			"ConstantValue" => {
				let idx: u16 = s.read()?;
				Self::Constant(constants.get(idx as usize)?.to_constant(constants)?)
			}
			_ => {
				s.advance(length as _);
				Self::NotSurport(name)
			}
		})
	}
	pub fn to_constant(&self) -> Option<&AttrConstant> {
		match self {
			Self::Constant(v) => Some(v),
			_ => None,
		}
	}
}
#[derive(Debug, Clone)]
pub enum AttrConstant {
	Long(i64),
	Float(f32),
	Double(f64),
	Interger(i32),
	String(String),
}
impl AttrConstant {
	pub fn fmt_value<'a>(&'a self, tag: &'a str) -> FmtValue<'a> {
		FmtValue(self, tag)
	}
	pub fn fmt_type<'a>(&'a self, tag: &'a str) -> FmtType<'a> {
		FmtType(self, tag)
	}
}
pub struct FmtValue<'a>(&'a AttrConstant, &'a str);
impl<'a> std::fmt::Display for FmtValue<'a> {
	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
		use AttrConstant::*;
		match self.0 {
			Long(v) => f.write_fmt(format_args!("{}i64", v)),
			Float(v) => f.write_fmt(format_args!("{}f32", v)),
			Double(v) => f.write_fmt(format_args!("{}f64", v)),
			Interger(v) => f.write_fmt(format_args!("{}{}", v, self.1)),
			String(v) => f.write_fmt(format_args!("\"{}\"", v)),
		}
	}
}

pub struct FmtType<'a>(&'a AttrConstant, &'a str);
impl<'a> std::fmt::Display for FmtType<'a> {
	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
		use AttrConstant::*;
		match self.0 {
			Long(_) => f.write_fmt(format_args!("i64")),
			Float(_) => f.write_fmt(format_args!("f32")),
			Double(_) => f.write_fmt(format_args!("f64")),
			Interger(_) => f.write_fmt(format_args!("{}", self.1)),
			String(_) => f.write_fmt(format_args!("&'static str")),
		}
	}
}
